package com.lifeverse.service;

import com.lifeverse.entity.LifeEntity;
import com.lifeverse.entity.Resource;
import com.lifeverse.entity.ResourceAllocation;
import com.lifeverse.entity.enums.ResourceStatus;
import com.lifeverse.entity.enums.ResourceType;
import com.lifeverse.repository.LifeEntityRepository;
import com.lifeverse.repository.ResourceAllocationRepository;
import com.lifeverse.repository.ResourceRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 资源管理服务
 * 提供资源管理的业务逻辑
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ResourceService {
    
    private final ResourceRepository resourceRepository;
    private final ResourceAllocationRepository allocationRepository;
    private final LifeEntityRepository lifeEntityRepository;
    private final ResourceAllocator resourceAllocator;
    
    /**
     * 创建资源
     */
    @Transactional
    public Resource createResource(ResourceCreateRequest request) {
        log.info("创建资源 - 名称: {}, 类型: {}, 容量: {}", 
                request.getName(), request.getType(), request.getCapacity());
        
        // 检查资源名称是否已存在
        if (resourceRepository.findByName(request.getName()).isPresent()) {
            throw new RuntimeException("资源名称已存在: " + request.getName());
        }
        
        Resource resource = new Resource();
        resource.setName(request.getName());
        resource.setType(request.getType());
        resource.setDescription(request.getDescription());
        resource.setCapacity(request.getCapacity());
        resource.setAvailableAmount(request.getCapacity());
        resource.setUnit(request.getUnit());
        resource.setUnitCost(request.getUnitCost());
        resource.setPriority(request.getPriority());
        resource.setLocation(request.getLocation());
        resource.setTags(request.getTags());
        resource.setMinAllocationUnit(request.getMinAllocationUnit());
        resource.setMaxAllocationUnit(request.getMaxAllocationUnit());
        resource.setExpiresAt(request.getExpiresAt());
        resource.setCreatedTime(LocalDateTime.now());
        
        if (request.getOwnerId() != null) {
            LifeEntity owner = lifeEntityRepository.findById(request.getOwnerId())
                    .orElseThrow(() -> new RuntimeException("拥有者不存在: " + request.getOwnerId()));
            resource.setOwner(owner);
        }
        
        Resource savedResource = resourceRepository.save(resource);
        log.info("资源创建成功 - ID: {}, 名称: {}", savedResource.getId(), savedResource.getName());
        
        return savedResource;
    }
    
    /**
     * 获取资源详情
     */
    public Resource getResource(Long resourceId) {
        return resourceRepository.findById(resourceId)
                .orElseThrow(() -> new RuntimeException("资源不存在: " + resourceId));
    }
    
    /**
     * 更新资源
     */
    @Transactional
    public Resource updateResource(Long resourceId, ResourceUpdateRequest request) {
        log.info("更新资源 - ID: {}", resourceId);
        
        Resource resource = getResource(resourceId);
        
        if (request.getDescription() != null) {
            resource.setDescription(request.getDescription());
        }
        if (request.getUnitCost() != null) {
            resource.setUnitCost(request.getUnitCost());
        }
        if (request.getPriority() != null) {
            resource.setPriority(request.getPriority());
        }
        if (request.getLocation() != null) {
            resource.setLocation(request.getLocation());
        }
        if (request.getTags() != null) {
            resource.setTags(request.getTags());
        }
        if (request.getExpiresAt() != null) {
            resource.setExpiresAt(request.getExpiresAt());
        }
        if (request.getNextMaintenanceAt() != null) {
            resource.setNextMaintenanceAt(request.getNextMaintenanceAt());
        }
        
        Resource savedResource = resourceRepository.save(resource);
        log.info("资源更新成功 - ID: {}", resourceId);
        
        return savedResource;
    }
    
    /**
     * 删除资源
     */
    @Transactional
    public void deleteResource(Long resourceId) {
        log.info("删除资源 - ID: {}", resourceId);
        
        Resource resource = getResource(resourceId);
        
        // 检查是否有活跃的分配
        List<ResourceAllocation> activeAllocations = allocationRepository.findActiveAllocationsByResource(
                resource, Arrays.asList("ACTIVE", "IN_USE"));
        
        if (!activeAllocations.isEmpty()) {
            throw new RuntimeException("资源有活跃的分配，无法删除");
        }
        
        resourceRepository.delete(resource);
        log.info("资源删除成功 - ID: {}", resourceId);
    }
    
    /**
     * 获取资源列表
     */
    public Page<Resource> getResources(ResourceQueryRequest request, Pageable pageable) {
        if (request.getType() != null) {
            return resourceRepository.findByType(request.getType(), pageable);
        }
        
        if (request.getOwnerId() != null) {
            LifeEntity owner = lifeEntityRepository.findById(request.getOwnerId())
                    .orElseThrow(() -> new RuntimeException("拥有者不存在: " + request.getOwnerId()));
            return resourceRepository.findByOwner(owner, pageable);
        }
        
        return resourceRepository.findAll(pageable);
    }
    
    /**
     * 搜索资源
     */
    public List<Resource> searchResources(String keyword) {
        return resourceRepository.findByKeyword(keyword);
    }
    
    /**
     * 获取可用资源
     */
    public List<Resource> getAvailableResources(ResourceType type) {
        List<ResourceStatus> availableStatuses = Arrays.asList(ResourceStatus.AVAILABLE, ResourceStatus.RECYCLED);
        
        if (type != null) {
            return resourceRepository.findAvailableResourcesByType(type, availableStatuses);
        } else {
            return resourceRepository.findAvailableResources(availableStatuses);
        }
    }
    
    /**
     * 获取资源统计信息
     */
    public ResourceStatistics getResourceStatistics(ResourceType type) {
        ResourceStatistics stats = new ResourceStatistics();
        stats.setResourceType(type);
        
        if (type != null) {
            stats.setTotalResources(resourceRepository.countByType(type));
            stats.setTotalCapacity(resourceRepository.sumCapacityByType(type));
            stats.setUsedAmount(resourceRepository.sumUsedAmountByType(type));
            stats.setAvailableAmount(resourceRepository.sumAvailableAmountByType(type));
        } else {
            stats.setTotalResources(resourceRepository.count());
            // 计算所有类型的总计
            BigDecimal totalCapacity = BigDecimal.ZERO;
            BigDecimal totalUsed = BigDecimal.ZERO;
            BigDecimal totalAvailable = BigDecimal.ZERO;
            
            for (ResourceType resourceType : ResourceType.values()) {
                BigDecimal capacity = resourceRepository.sumCapacityByType(resourceType);
                BigDecimal used = resourceRepository.sumUsedAmountByType(resourceType);
                BigDecimal available = resourceRepository.sumAvailableAmountByType(resourceType);
                
                if (capacity != null) totalCapacity = totalCapacity.add(capacity);
                if (used != null) totalUsed = totalUsed.add(used);
                if (available != null) totalAvailable = totalAvailable.add(available);
            }
            
            stats.setTotalCapacity(totalCapacity);
            stats.setUsedAmount(totalUsed);
            stats.setAvailableAmount(totalAvailable);
        }
        
        // 计算使用率
        if (stats.getTotalCapacity() != null && stats.getTotalCapacity().compareTo(BigDecimal.ZERO) > 0) {
            BigDecimal usageRate = stats.getUsedAmount().divide(stats.getTotalCapacity(), 4, java.math.RoundingMode.HALF_UP);
            stats.setUsageRate(usageRate);
        }
        
        // 统计各状态的资源数量
        Map<ResourceStatus, Long> statusCounts = new HashMap<>();
        for (ResourceStatus status : ResourceStatus.values()) {
            long count = resourceRepository.countByStatus(status);
            statusCounts.put(status, count);
        }
        stats.setStatusCounts(statusCounts);
        
        return stats;
    }
    
    /**
     * 获取资源分配历史
     */
    public Page<ResourceAllocation> getResourceAllocations(Long resourceId, Pageable pageable) {
        Resource resource = getResource(resourceId);
        return allocationRepository.findByResource(resource, pageable);
    }
    
    /**
     * 获取生命体的资源分配
     */
    public Page<ResourceAllocation> getLifeEntityAllocations(Long lifeEntityId, Pageable pageable) {
        LifeEntity lifeEntity = lifeEntityRepository.findById(lifeEntityId)
                .orElseThrow(() -> new RuntimeException("生命体不存在: " + lifeEntityId));
        return allocationRepository.findByAllocatedTo(lifeEntity, pageable);
    }
    
    /**
     * 设置资源维护模式
     */
    @Transactional
    public Resource setMaintenanceMode(Long resourceId, boolean maintenance, String reason) {
        log.info("设置资源维护模式 - ID: {}, 维护: {}", resourceId, maintenance);
        
        Resource resource = getResource(resourceId);
        resource.setMaintenanceMode(maintenance);
        
        if (maintenance) {
            // 如果进入维护模式，释放所有活跃分配
            List<ResourceAllocation> activeAllocations = allocationRepository.findActiveAllocationsByResource(
                    resource, Arrays.asList("ACTIVE", "IN_USE"));
            
            for (ResourceAllocation allocation : activeAllocations) {
                allocation.cancel("资源进入维护模式: " + reason);
                allocationRepository.save(allocation);
            }
            
            // 重置资源使用量
            resource.setUsedAmount(BigDecimal.ZERO);
            resource.setAvailableAmount(resource.getCapacity());
            resource.setCurrentUser(null);
        }
        
        Resource savedResource = resourceRepository.save(resource);
        log.info("资源维护模式设置成功 - ID: {}, 维护: {}", resourceId, maintenance);
        
        return savedResource;
    }
    
    /**
     * 扩展资源容量
     */
    @Transactional
    public Resource expandCapacity(Long resourceId, BigDecimal additionalCapacity) {
        log.info("扩展资源容量 - ID: {}, 增加容量: {}", resourceId, additionalCapacity);
        
        if (additionalCapacity == null || additionalCapacity.compareTo(BigDecimal.ZERO) <= 0) {
            throw new RuntimeException("扩展容量必须大于0");
        }
        
        Resource resource = getResource(resourceId);
        
        BigDecimal newCapacity = resource.getCapacity().add(additionalCapacity);
        BigDecimal newAvailable = resource.getAvailableAmount().add(additionalCapacity);
        
        resource.setCapacity(newCapacity);
        resource.setAvailableAmount(newAvailable);
        
        // 如果资源之前是耗尽状态，现在变为可用
        if (resource.getStatus() == ResourceStatus.EXHAUSTED) {
            resource.setStatus(ResourceStatus.AVAILABLE);
        }
        
        Resource savedResource = resourceRepository.save(resource);
        log.info("资源容量扩展成功 - ID: {}, 新容量: {}", resourceId, newCapacity);
        
        return savedResource;
    }
    
    /**
     * 回收资源
     */
    @Transactional
    public Resource recycleResource(Long resourceId) {
        log.info("回收资源 - ID: {}", resourceId);
        
        Resource resource = getResource(resourceId);
        
        // 取消所有活跃分配
        List<ResourceAllocation> activeAllocations = allocationRepository.findActiveAllocationsByResource(
                resource, Arrays.asList("ACTIVE", "IN_USE"));
        
        for (ResourceAllocation allocation : activeAllocations) {
            allocation.cancel("资源被回收");
            allocationRepository.save(allocation);
        }
        
        // 重置资源状态
        resource.setStatus(ResourceStatus.RECYCLED);
        resource.setUsedAmount(BigDecimal.ZERO);
        resource.setAvailableAmount(resource.getCapacity());
        resource.setCurrentUser(null);
        
        Resource savedResource = resourceRepository.save(resource);
        log.info("资源回收成功 - ID: {}", resourceId);
        
        return savedResource;
    }
    
    /**
     * 定期维护任务
     */
    @Scheduled(fixedRate = 3600000) // 每小时执行一次
    public void performScheduledMaintenance() {
        log.debug("执行定期资源维护任务");
        
        try {
            // 处理需要维护的资源
            handleResourcesMaintenance();
            
            // 处理过期的资源
            handleExpiredResources();
            
            // 处理过期的分配
            handleExpiredAllocations();
            
            // 清理长时间未使用的资源
            cleanupUnusedResources();
            
        } catch (Exception e) {
            log.error("定期维护任务执行失败", e);
        }
    }
    
    /**
     * 处理需要维护的资源
     */
    private void handleResourcesMaintenance() {
        List<Resource> resourcesNeedingMaintenance = resourceRepository.findResourcesNeedingMaintenance(LocalDateTime.now());
        
        for (Resource resource : resourcesNeedingMaintenance) {
            try {
                setMaintenanceMode(resource.getId(), true, "定期维护");
                log.info("资源进入维护模式 - ID: {}, 名称: {}", resource.getId(), resource.getName());
            } catch (Exception e) {
                log.error("设置资源维护模式失败 - ID: {}", resource.getId(), e);
            }
        }
    }
    
    /**
     * 处理过期的资源
     */
    private void handleExpiredResources() {
        List<Resource> expiredResources = resourceRepository.findExpiredResources(LocalDateTime.now());
        
        for (Resource resource : expiredResources) {
            try {
                resource.setStatus(ResourceStatus.RETIRED);
                resourceRepository.save(resource);
                log.info("资源已过期并退役 - ID: {}, 名称: {}", resource.getId(), resource.getName());
            } catch (Exception e) {
                log.error("处理过期资源失败 - ID: {}", resource.getId(), e);
            }
        }
    }
    
    /**
     * 处理过期的分配
     */
    private void handleExpiredAllocations() {
        List<ResourceAllocation> expiredAllocations = allocationRepository.findExpiredAllocations(LocalDateTime.now());
        
        for (ResourceAllocation allocation : expiredAllocations) {
            try {
                resourceAllocator.releaseResource(allocation.getId(), null);
                log.info("过期分配已释放 - ID: {}", allocation.getId());
            } catch (Exception e) {
                log.error("释放过期分配失败 - ID: {}", allocation.getId(), e);
            }
        }
    }
    
    /**
     * 清理长时间未使用的资源
     */
    private void cleanupUnusedResources() {
        LocalDateTime threshold = LocalDateTime.now().minusDays(30); // 30天未使用
        List<Resource> unusedResources = resourceRepository.findUnusedResources(threshold);
        
        for (Resource resource : unusedResources) {
            try {
                if (resource.getStatus() == ResourceStatus.AVAILABLE) {
                    resource.setStatus(ResourceStatus.RECYCLED);
                    resourceRepository.save(resource);
                    log.debug("长时间未使用的资源已回收 - ID: {}, 名称: {}", resource.getId(), resource.getName());
                }
            } catch (Exception e) {
                log.error("回收未使用资源失败 - ID: {}", resource.getId(), e);
            }
        }
    }
    
    // 内部类定义
    public static class ResourceCreateRequest {
        private String name;
        private ResourceType type;
        private String description;
        private BigDecimal capacity;
        private String unit;
        private BigDecimal unitCost;
        private Integer priority = 5;
        private String location;
        private String tags;
        private BigDecimal minAllocationUnit;
        private BigDecimal maxAllocationUnit;
        private LocalDateTime expiresAt;
        private Long ownerId;
        
        // getters and setters
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public ResourceType getType() { return type; }
        public void setType(ResourceType type) { this.type = type; }
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        public BigDecimal getCapacity() { return capacity; }
        public void setCapacity(BigDecimal capacity) { this.capacity = capacity; }
        public String getUnit() { return unit; }
        public void setUnit(String unit) { this.unit = unit; }
        public BigDecimal getUnitCost() { return unitCost; }
        public void setUnitCost(BigDecimal unitCost) { this.unitCost = unitCost; }
        public Integer getPriority() { return priority; }
        public void setPriority(Integer priority) { this.priority = priority; }
        public String getLocation() { return location; }
        public void setLocation(String location) { this.location = location; }
        public String getTags() { return tags; }
        public void setTags(String tags) { this.tags = tags; }
        public BigDecimal getMinAllocationUnit() { return minAllocationUnit; }
        public void setMinAllocationUnit(BigDecimal minAllocationUnit) { this.minAllocationUnit = minAllocationUnit; }
        public BigDecimal getMaxAllocationUnit() { return maxAllocationUnit; }
        public void setMaxAllocationUnit(BigDecimal maxAllocationUnit) { this.maxAllocationUnit = maxAllocationUnit; }
        public LocalDateTime getExpiresAt() { return expiresAt; }
        public void setExpiresAt(LocalDateTime expiresAt) { this.expiresAt = expiresAt; }
        public Long getOwnerId() { return ownerId; }
        public void setOwnerId(Long ownerId) { this.ownerId = ownerId; }
    }
    
    public static class ResourceUpdateRequest {
        private String description;
        private BigDecimal unitCost;
        private Integer priority;
        private String location;
        private String tags;
        private LocalDateTime expiresAt;
        private LocalDateTime nextMaintenanceAt;
        
        // getters and setters
        public String getDescription() { return description; }
        public void setDescription(String description) { this.description = description; }
        public BigDecimal getUnitCost() { return unitCost; }
        public void setUnitCost(BigDecimal unitCost) { this.unitCost = unitCost; }
        public Integer getPriority() { return priority; }
        public void setPriority(Integer priority) { this.priority = priority; }
        public String getLocation() { return location; }
        public void setLocation(String location) { this.location = location; }
        public String getTags() { return tags; }
        public void setTags(String tags) { this.tags = tags; }
        public LocalDateTime getExpiresAt() { return expiresAt; }
        public void setExpiresAt(LocalDateTime expiresAt) { this.expiresAt = expiresAt; }
        public LocalDateTime getNextMaintenanceAt() { return nextMaintenanceAt; }
        public void setNextMaintenanceAt(LocalDateTime nextMaintenanceAt) { this.nextMaintenanceAt = nextMaintenanceAt; }
    }
    
    public static class ResourceQueryRequest {
        private ResourceType type;
        private ResourceStatus status;
        private Long ownerId;
        private String location;
        private String keyword;
        
        // getters and setters
        public ResourceType getType() { return type; }
        public void setType(ResourceType type) { this.type = type; }
        public ResourceStatus getStatus() { return status; }
        public void setStatus(ResourceStatus status) { this.status = status; }
        public Long getOwnerId() { return ownerId; }
        public void setOwnerId(Long ownerId) { this.ownerId = ownerId; }
        public String getLocation() { return location; }
        public void setLocation(String location) { this.location = location; }
        public String getKeyword() { return keyword; }
        public void setKeyword(String keyword) { this.keyword = keyword; }
    }
    
    public static class ResourceStatistics {
        private ResourceType resourceType;
        private long totalResources;
        private BigDecimal totalCapacity;
        private BigDecimal usedAmount;
        private BigDecimal availableAmount;
        private BigDecimal usageRate;
        private Map<ResourceStatus, Long> statusCounts;
        
        // getters and setters
        public ResourceType getResourceType() { return resourceType; }
        public void setResourceType(ResourceType resourceType) { this.resourceType = resourceType; }
        public long getTotalResources() { return totalResources; }
        public void setTotalResources(long totalResources) { this.totalResources = totalResources; }
        public BigDecimal getTotalCapacity() { return totalCapacity; }
        public void setTotalCapacity(BigDecimal totalCapacity) { this.totalCapacity = totalCapacity; }
        public BigDecimal getUsedAmount() { return usedAmount; }
        public void setUsedAmount(BigDecimal usedAmount) { this.usedAmount = usedAmount; }
        public BigDecimal getAvailableAmount() { return availableAmount; }
        public void setAvailableAmount(BigDecimal availableAmount) { this.availableAmount = availableAmount; }
        public BigDecimal getUsageRate() { return usageRate; }
        public void setUsageRate(BigDecimal usageRate) { this.usageRate = usageRate; }
        public Map<ResourceStatus, Long> getStatusCounts() { return statusCounts; }
        public void setStatusCounts(Map<ResourceStatus, Long> statusCounts) { this.statusCounts = statusCounts; }
    }
}